package org.jeecg.websocket.impl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.naming.AuthenticationException;

import com.corundumstudio.socketio.protocol.Packet;
import io.netty.channel.Channel;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.websocket.DeviceConstant;
import org.jeecg.websocket.IShiroRealm;
import org.jeecg.websocket.SocketIOService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;

@Slf4j
@Service(value = "socketIOService")
public class SocketIOServiceImpl implements SocketIOService {

    // 用来存已连接的客户端
    private static Map<String, Map<DeviceConstant,SocketIOClient>> clientMap = new ConcurrentHashMap<>();

    @Autowired
    private SocketIOServer socketIOServer;

    /**
     * Spring IoC容器创建之后，在加载SocketIOServiceImpl Bean之后启动
     * @throws Exception
     */
    @PostConstruct
    private void autoStartup() throws Exception {
        start();
    }

    /**
     * Spring IoC容器在销毁SocketIOServiceImpl Bean之前关闭,避免重启项目服务端口占用问题
     * @throws Exception
     */
    @PreDestroy
    private void autoStop() throws Exception  {
        stop();
    }

    @Override
    public void start() {
        // 监听客户端连接
        socketIOServer.addConnectListener(client -> {
            Params params = getParamsByClient(client);
            if (params != null) {
                Map<DeviceConstant, SocketIOClient> ofUser = clientMap.get(params.getUserId());
                if (ofUser==null){
                    ofUser = new HashMap<>(3);
                    clientMap.put(params.getUserId(), ofUser);
                }
                ofUser.put(params.getDevice(), client);
            }
        });

        // 监听客户端断开连接
        socketIOServer.addDisconnectListener(client -> {
            Params params = getParamsByClient(client);
            if (params != null) {
                Map<DeviceConstant, SocketIOClient> ofUser = clientMap.get(params.getUserId());
                if (ofUser!=null){
                    ofUser.remove(params.getDevice());
                }
            }
            client.disconnect();
        });

        // 处理自定义的事件，与连接监听类似
//        socketIOServer.addEventListener(PUSH_EVENT, PushMessage.class, (client, data, ackSender) -> {
//            //
//        });
        socketIOServer.start();
    }

    @Override
    public void stop() {
        if (socketIOServer != null) {
            socketIOServer.stop();
            socketIOServer = null;
        }
    }

    @Override
    public int pushMessageToUser(String userId,String event, String  pushMessage) {
        int r = 0;
        Map<DeviceConstant, SocketIOClient> clients = clientMap.get(userId);
        if (clients != null){
            for (SocketIOClient c : clients.values()){
                try {
                    c.sendEvent(event, pushMessage);
                    r++;
                } catch (Exception e) {
                    log.error("userId:" + userId + " msg:" + pushMessage, e);
                }
            }

        }
        log.info("发送 通道:" + r + " event:" + event + " 用户:" + userId + " 内容:" + pushMessage);
        return r;
    }

    /**
     * 此方法为获取client连接中的参数，可根据需求更改
     * @param client
     * @return
     */
    private Params getParamsByClient(SocketIOClient client) {
        // 从请求的连接中拿出参数（这里的loginUserNum必须是唯一标识）
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        Params res  = new Params();
        List<String> list =  params.get("device");
        if (list != null && list.size() > 0) {
            res.setDevice(DeviceConstant.valueOf(list.get(0).toUpperCase()));
        }
        list = params.get("token");
        if (list != null && list.size() > 0) {
            res.setUserId(getUserId(list.get(0)));
        }
        if (res.getDevice()==null || res.getUserId()==null) return null;
        return res;
    }

    /**
     * 需要先根据token到sso获取userId
     * @param token
     * @return
     */
    public String getUserId(String token){
        if (token==null) return null;
        String id = null;
        try {
            LoginUser u = iShiroRealm.checkUserTokenIsEffect(token);
            if (u!=null) id =  u.getId();
        } catch (AuthenticationException e) {
            log.error("获取用户信息异常, token:" + token, e);
        }
        return id;
    }
    @Autowired
    IShiroRealm iShiroRealm;
    @Data
    public static class Params{
        String userId;
        DeviceConstant device;
    }
}